/*
 * Copyright (c) 2013-2015 SimaStudio. All rights reserved.
 *
 * This software is the confidential and proprietary information of SimaStudio.
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the agreements you entered into with SimaStudio.
 *
 * SimaStudio MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 * SimaStudio SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package com.asiainfo.integration.quartz;

import com.asiainfo.integration.enums.JobStatus;
import com.asiainfo.integration.utils.SpringUtils;
import com.asiainfo.integration.xml.Map2XmlUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

@SuppressWarnings("SpringJavaAutowiringInspection")
@Service
public class ScheduleJobService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleJobService.class);

    private static final URL url = ScheduleJobService.class.getClassLoader().getResource("config/task/task_details.xml");

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    private static Map<Integer, ScheduleJob> scheduleJobMap = new TreeMap<Integer, ScheduleJob>();

    /**
     * 从数据库中取 区别于getAllJob
     *
     * @return
     */
    public List<ScheduleJob> getAllTask() {
        if (scheduleJobMap.size() > 0) {
            List<ScheduleJob> scheduleJobs = new ArrayList<ScheduleJob>();
            for (ScheduleJob scheduleJob : scheduleJobMap.values()) {
                scheduleJobs.add(scheduleJob);
            }
            return scheduleJobs;
        } else {
            try {
                SAXReader reader = new SAXReader();
                InputStream in = new FileInputStream(new File(url.toURI().getPath()));
                Document inDocument = reader.read(in);

                Element root = inDocument.getRootElement();
                List<Element> tasks = root.elements();
                for (Element element : tasks) {
                    ScheduleJob scheduleJob = new ScheduleJob();
                    scheduleJob.xmlElementToAttribute(element);
                    scheduleJobMap.put(Integer.parseInt(element.element("id").getText()), scheduleJob);
                }
                List<ScheduleJob> scheduleJobs = new ArrayList<ScheduleJob>();
                for (ScheduleJob scheduleJob : scheduleJobMap.values()) {
                    scheduleJobs.add(scheduleJob);
                }
                return scheduleJobs;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (URISyntaxException e) {
                e.printStackTrace();
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 区别于addJob
     */
    public void addTask(ScheduleJob job) {
        int nextId = scheduleJobMap.size() + 1;
        job.setId(nextId);
        scheduleJobMap.put(nextId, job);
        Map2XmlUtils.scheduleJobMapToXml("tasks", "taskDetails", scheduleJobMap, url);
    }

    public ScheduleJob getTaskById(Integer jobId) {
        return scheduleJobMap.get(jobId);
    }

    /**
     * 更改任务状态
     *
     * @throws SchedulerException
     */
    public void changeStatus(Integer jobId, String cmd) throws SchedulerException {
        ScheduleJob job = getTaskById(jobId);
        if (job == null) {
            return;
        }
        if ("stop".equals(cmd)) {
            deleteJob(job);
            job.setStatus(JobStatus.Stopped.getValue());
        } else if ("start".equals(cmd)) {
            job.setStatus(JobStatus.Running.getValue());
            addJob(job);
        }
        scheduleJobMap.put(jobId, job);
        Map2XmlUtils.scheduleJobMapToXml("tasks", "taskDetails", scheduleJobMap, url);
    }

    /**
     * 更改任务 cron表达式
     *
     * @throws SchedulerException
     */
    public void updateCron(Integer jobId, String cron) throws SchedulerException {
        ScheduleJob job = getTaskById(jobId);
        if (job == null) {
            return;
        }
        job.setCronExpression(cron);
        if (JobStatus.Running.getValue().equals(job.getStatus())) {
            updateJobCron(job);
        }
        scheduleJobMap.put(jobId, job);
        Map2XmlUtils.scheduleJobMapToXml("tasks", "taskDetails", scheduleJobMap, url);
    }

    /**
     * 添加任务
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void addJob(ScheduleJob scheduleJob) throws SchedulerException {
        if (scheduleJob == null || !JobStatus.Running.getValue().equals(scheduleJob.getStatus())) {
            return;
        }
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (null == trigger) {
            Class clazz = null;
            if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
                try {
                    clazz = Class.forName(scheduleJob.getBeanClass());
                } catch (ClassNotFoundException e) {
                    LOGGER.error("ScheduleJob initialize error, ClassNotFoundException: " + scheduleJob.getBeanClass());
                }
            } else if (StringUtils.isNotBlank(scheduleJob.getBeanName())) {
                clazz = SpringUtils.getType(scheduleJob.getBeanName());

            }
            @SuppressWarnings("unchecked")
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
            jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
            // 按新的cronExpression表达式构建一个新的trigger
            trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            // Trigger已存在，那么更新相应的定时设置
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    @PostConstruct
    public void init() throws Exception {
        getAllTask();
        // 这里获取任务信息数据
        for (ScheduleJob scheduleJob : scheduleJobMap.values()) {
            addJob(scheduleJob);
        }
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     * @throws SchedulerException
     */
    public List<ScheduleJob> getAllJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                ScheduleJob job = new ScheduleJob();
                job.setJobName(jobKey.getName());
                job.setJobGroup(jobKey.getGroup());
                job.setDescription("触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                // Todo: 检查一下这边是否设置正确
                job.setStatus(Integer.parseInt(triggerState.name()));
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                }
                jobList.add(job);
            }
        }
        return jobList;
    }

    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    public List<ScheduleJob> getRunningJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            ScheduleJob job = new ScheduleJob();
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            job.setDescription("触发器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            // Todo: 检查一下这边是否设置正确
            job.setStatus(Integer.parseInt(triggerState.name()));
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCronExpression(cronExpression);
            }
            jobList.add(job);
        }
        return jobList;
    }

    /**
     * 暂停一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.deleteJob(jobKey);
    }

    /**
     * 立即执行job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新job时间表达式
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        scheduler.rescheduleJob(triggerKey, trigger);
    }
}
